<?php

declare(strict_types=1);
/**
 * This file is part of MineAdmin.
 *
 * @link     https://www.mineadmin.com
 * @document https://doc.mineadmin.com
 * @contact  root@imoi.cn
 * @license  https://github.com/mineadmin/MineAdmin/blob/master/LICENSE
 */

namespace App\System\Service;

use App\Constants\OrderPayStatus;
use App\Model\Member;
use App\Model\Order;
use App\System\Mapper\SystemUserMapper;
use Carbon\Carbon;
use Hyperf\Cache\Annotation\Cacheable;
use Hyperf\Cache\Annotation\CacheEvict;
use Hyperf\Contract\ContainerInterface;
use Hyperf\Di\Annotation\Inject;
use Mine\Abstracts\AbstractService;
use Mine\Annotation\DependProxy;
use Mine\Event\UserAdd;
use Mine\Event\UserDelete;
use Mine\Exception\MineException;
use Mine\Exception\NormalStatusException;
use Mine\Interfaces\ServiceInterface\UserServiceInterface;
use Mine\MineRequest;
use Psr\Container\ContainerExceptionInterface;
use Psr\Container\NotFoundExceptionInterface;
use Psr\SimpleCache\InvalidArgumentException;

/**
 * 用户业务
 * Class SystemUserService.
 */
#[DependProxy(values: [UserServiceInterface::class])]
class SystemUserService extends AbstractService implements UserServiceInterface
{
    /**
     * @var SystemUserMapper
     */
    public $mapper;

    #[Inject]
    protected MineRequest $request;

    protected ContainerInterface $container;

    protected SystemMenuService $sysMenuService;

    protected SystemRoleService $sysRoleService;

    /**
     * SystemUserService constructor.
     */
    public function __construct(
        ContainerInterface $container,
        SystemUserMapper $mapper,
        SystemMenuService $systemMenuService,
        SystemRoleService $systemRoleService
    ) {
        $this->mapper = $mapper;
        $this->sysMenuService = $systemMenuService;
        $this->sysRoleService = $systemRoleService;
        $this->container = $container;
    }

    /**
     * 获取用户信息.
     * @throws ContainerExceptionInterface
     * @throws NotFoundExceptionInterface
     */
    public function getInfo(): array
    {
        if ($uid = user()->getId()) {
            $data = $this->getCacheInfo((int) $uid);
            if ($data['user']['dashboard'] === 'statistics') {
                $data['user']['statistics'] = $this->getStatistics();
            }
            return $data;
        }
        throw new MineException(t('system.unable_get_userinfo'), 500);
    }

    private function getStatistics(): array
    {
        // 当前时间
        $now = Carbon::now();

        // 昨日开始结束时间
        $yesterdayStart = $now->copy()->subDays(1)->startOfDay();
        $yesterdayEnd = $yesterdayStart->copy()->endOfDay();
        // 昨日订单、用户
        $yesterdayOrderCount = Order::query()
            ->where('pay_status', OrderPayStatus::SUCCESS)
            ->whereBetween('created_at', [$yesterdayStart, $yesterdayEnd])
            ->count();
        $yesterdayMemberCount = Member::query()
            ->whereBetween('created_at', [$yesterdayStart, $yesterdayEnd])
            ->count();

        // 本月开始结束时间
        $monthStart = $now->copy()->startOfMonth();
        $monthEnd = $monthStart->copy()->endOfMonth();
        // 本月订单、用户
        $monthOrderCount = Order::query()
            ->where('pay_status', OrderPayStatus::SUCCESS)
            ->whereBetween('created_at', [$monthStart, $monthEnd])
            ->count();
        $monthMemberCount = Member::query()
            ->whereBetween('created_at', [$monthStart, $monthEnd])
            ->count();

        // 最近开始结束时间
        $recentlyStart = $now->copy()->subDays(6)->startOfDay();
        $recentlyEnd = $now->copy()->endOfDay();
        // 最近7天订单
        $recentlyOrders = Order::query()
            ->where('pay_status', OrderPayStatus::SUCCESS)
            ->whereBetween('created_at', [$recentlyStart, $recentlyEnd])
            ->selectRaw('DATE_FORMAT(`created_at`, "%Y-%m-%d") `date`,COUNT(*) `amount`')
            ->groupBy('date')
            ->pluck('amount', 'date');
        $recentlyStartTime = $recentlyStart->timestamp;
        $recentlyLabels = [];
        $recentlyValues = [];
        for ($i = 0; $i < 7; $i++) {
            $tempDate = date('Y-m-d', $recentlyStartTime + ($i * 86400));
            $recentlyLabels[] = date('m-d', (int)strtolower($tempDate));
            $recentlyValues[] = $recentlyOrders[$tempDate] ?? 0;
        }

        // 最新5条订单
        $latestOrders = Order::query()
            ->with(['orderCar:order_id,describe', 'member:id,nickname'])
            ->where('pay_status', OrderPayStatus::SUCCESS)
            ->latest('id')
            ->limit(5)
            ->get()
            ->each(function (Order $order) {
                $order->car_describe = $order->orderCar->describe;
                $order->member_nickname = $order->member->nickname;
                $order->setVisible(['car_describe', 'total_price', 'member_nickname', 'created_at']);
            });

        // 总计订单、用户、总额
        $totalOrder = Order::query()
            ->where('pay_status', OrderPayStatus::SUCCESS)
            ->selectRaw('COUNT(*) amount,SUM(`total_price`) money')
            ->first();
        $totalMemberCount = Member::query()->count();

        return [
            'yesterday_order_count' => $yesterdayOrderCount,
            'yesterday_member_count' => $yesterdayMemberCount,
            'month_order_count' => $monthOrderCount,
            'month_member_count' => $monthMemberCount,
            'recently_orders' => [$recentlyLabels, $recentlyValues],
            'latest_orders' => $latestOrders,
            'total_order_count' => $totalOrder->amount ?? 0,
            'total_order_money' => $totalOrder->money ?? 0,
            'total_member_count' => $totalMemberCount,
        ];
    }

    /**
     * 新增用户.
     * @throws ContainerExceptionInterface
     * @throws NotFoundExceptionInterface
     */
    public function save(array $data): mixed
    {
        if ($this->mapper->existsByUsername($data['username'])) {
            throw new NormalStatusException(t('system.username_exists'));
        }
        $id = $this->mapper->save($this->handleData($data));
        $data['id'] = $id;
        event(new UserAdd($data));
        return $id;
    }

    /**
     * 更新用户信息.
     */
    #[CacheEvict(prefix: 'loginInfo', value: 'userId_#{id}')]
    public function update(mixed $id, array $data): bool
    {
        if (isset($data['username'])) {
            unset($data['username']);
        }
        if (isset($data['password'])) {
            unset($data['password']);
        }
        return $this->mapper->update($id, $this->handleData($data));
    }

    /**
     * 获取在线用户.
     * @throws ContainerExceptionInterface
     * @throws NotFoundExceptionInterface
     */
    public function getOnlineUserPageList(array $params = []): array
    {
        $redis = redis();
        $key = sprintf('%sToken:*', config('cache.default.prefix'));

        $userIds = [];
        $iterator = null;

        while (false !== ($users = $redis->scan($iterator, $key, 100))) {
            foreach ($users as $user) {
                if (preg_match("/{$key}(\\d+)$/", $user, $match) && isset($match[1])) {
                    $userIds[] = $match[1];
                }
            }
            unset($users);
        }

        if (empty($userIds)) {
            return [];
        }

        return $this->getPageList(array_merge(['userIds' => $userIds], $params));
    }

    /**
     * 删除用户.
     * @throws ContainerExceptionInterface
     * @throws NotFoundExceptionInterface
     */
    public function delete(array $ids): bool
    {
        if (! empty($ids)) {
            if (($key = array_search(env('SUPER_ADMIN'), $ids)) !== false) {
                unset($ids[$key]);
            }
            $result = $this->mapper->delete($ids);
            event(new UserDelete($ids));
            return $result;
        }

        return false;
    }

    /**
     * 真实删除用户.
     * @throws ContainerExceptionInterface
     * @throws NotFoundExceptionInterface
     */
    public function realDelete(array $ids): bool
    {
        if (! empty($ids)) {
            if (($key = array_search(env('SUPER_ADMIN'), $ids)) !== false) {
                unset($ids[$key]);
            }
            $result = $this->mapper->realDelete($ids);
            event(new UserDelete($ids));
            return $result;
        }

        return false;
    }

    /**
     * 强制下线用户.
     * @throws InvalidArgumentException
     * @throws ContainerExceptionInterface
     * @throws NotFoundExceptionInterface
     */
    public function kickUser(string $id): bool
    {
        $redis = redis();
        $key = sprintf('%sToken:%s', config('cache.default.prefix'), $id);
        user()->getJwt()->logout($redis->get($key), 'default');
        $redis->del($key);
        return true;
    }

    /**
     * 初始化用户密码
     */
    public function initUserPassword(int $id, string $password = '123456'): bool
    {
        return $this->mapper->initUserPassword($id, $password);
    }

    /**
     * 清除用户缓存.
     * @throws ContainerExceptionInterface
     * @throws NotFoundExceptionInterface
     */
    public function clearCache(string $id): bool
    {
        $redis = redis();
        $prefix = config('cache.default.prefix');

        $iterator = null;
        while (false !== ($configKey = $redis->scan($iterator, $prefix . 'config:*', 100))) {
            $redis->del($configKey);
        }
        while (false !== ($dictKey = $redis->scan($iterator, $prefix . 'Dict:*', 100))) {
            $redis->del($dictKey);
        }
        $redis->del([$prefix . 'crontab', $prefix . 'modules']);

        return $redis->del("{$prefix}loginInfo:userId_{$id}") > 0;
    }

    /**
     * 设置用户首页.
     * @throws ContainerExceptionInterface
     * @throws NotFoundExceptionInterface
     */
    public function setHomePage(array $params): bool
    {
        $res = ($this->mapper->getModel())::query()
            ->where('id', $params['id'])
            ->update(['dashboard' => $params['dashboard']]) > 0;

        $this->clearCache((string) $params['id']);
        return $res;
    }

    /**
     * 用户更新个人资料.
     * @throws ContainerExceptionInterface
     * @throws NotFoundExceptionInterface
     */
    public function updateInfo(array $params): bool
    {
        if (! isset($params['id'])) {
            return false;
        }

        $model = $this->mapper->getModel()::find($params['id']);
        unset($params['id'], $params['password']);
        foreach ($params as $key => $param) {
            $model[$key] = $param;
        }

        $this->clearCache((string) $model['id']);
        return $model->save();
    }

    /**
     * 用户修改密码
     */
    public function modifyPassword(array $params): bool
    {
        return $this->mapper->initUserPassword((int) user()->getId(), $params['newPassword']);
    }

    /**
     * 通过 id 列表获取用户基础信息.
     */
    public function getUserInfoByIds(array $ids): array
    {
        return $this->mapper->getUserInfoByIds($ids);
    }

    /**
     * 获取缓存用户信息.
     */
    #[Cacheable(prefix: 'loginInfo', ttl: 0, value: 'userId_#{id}')]
    protected function getCacheInfo(int $id): array
    {
        $user = $this->mapper->getModel()->find($id);
        $user->addHidden('deleted_at', 'password');
        $data['user'] = $user->toArray();
        if (user()->isSuperAdmin()) {
            $data['roles'] = ['superAdmin'];
            $data['routers'] = $this->sysMenuService->mapper->getSuperAdminRouters();
            $data['codes'] = ['*'];
        } else {
            $roles = $this->sysRoleService->mapper->getMenuIdsByRoleIds($user->roles()->pluck('id')->toArray());
            $ids = $this->filterMenuIds($roles);
            $data['roles'] = $user->roles()->pluck('code')->toArray();
            $data['routers'] = $this->sysMenuService->mapper->getRoutersByIds($ids);
            $data['codes'] = $this->sysMenuService->mapper->getMenuCode($ids);
        }

        return $data;
    }

    /**
     * 过滤通过角色查询出来的菜单id列表，并去重.
     */
    protected function filterMenuIds(array &$roleData): array
    {
        $ids = [];
        foreach ($roleData as $val) {
            foreach ($val['menus'] as $menu) {
                $ids[] = $menu['id'];
            }
        }
        unset($roleData);
        return array_unique($ids);
    }

    /**
     * 处理提交数据.
     * @param mixed $data
     */
    protected function handleData($data): array
    {
        if (! is_array($data['role_ids'])) {
            $data['role_ids'] = explode(',', $data['role_ids']);
        }
        if (($key = array_search(env('ADMIN_ROLE'), $data['role_ids'])) !== false) {
            unset($data['role_ids'][$key]);
        }
        if (! empty($data['post_ids']) && ! is_array($data['post_ids'])) {
            $data['post_ids'] = explode(',', $data['post_ids']);
        }
        if (! empty($data['dept_ids']) && ! is_array($data['dept_ids'])) {
            $data['dept_ids'] = explode(',', $data['dept_ids']);
        }
        return $data;
    }
}
